package com.example.test;

import org.openhitls.crypto.HMAC;
import org.junit.Test;
import static org.junit.Assert.*;

import java.nio.charset.StandardCharsets;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class HMACTest {
    @Test
    public void testHMACSM3SingleShot() {
        String key = "TestKey123";
        String message = "Hello, HMAC-SM3!";
        byte[] mac = HMAC.compute(HMAC.HMAC_SM3, key.getBytes(), message.getBytes());
        assertNotNull("MAC should not be null", mac);

        assertEquals("MAC length should be 32 bytes", 32, mac.length);
    }

    @Test
    public void testHMACSM3Incremental() {
        String key = "TestKey123";
        HMAC hmac = new HMAC(HMAC.HMAC_SM3, key.getBytes());

        String part1 = "Hello, ";
        String part2 = "HMAC-SM3!";
        hmac.update(part1.getBytes());
        hmac.update(part2.getBytes());
        byte[] incrementalMac = hmac.doFinal();
        assertNotNull("Incremental MAC should not be null", incrementalMac);

        byte[] singleMac = HMAC.compute(HMAC.HMAC_SM3, key.getBytes(),
            (part1 + part2).getBytes());
        assertNotNull("Single-shot MAC should not be null", singleMac);

        assertArrayEquals(singleMac, incrementalMac);
    }

    @Test
    public void testEmptyString() {
        String key = "TestKey123";

        byte[] mac = HMAC.compute(HMAC.HMAC_SM3, key.getBytes(), new byte[0]);
        assertNotNull("MAC of empty string should not be null", mac);

        assertEquals("MAC length should be 32 bytes", 32, mac.length);
    }

    @Test
    public void testEmptyKey() {
        String message = "Test message";

        byte[] mac = HMAC.compute(HMAC.HMAC_SM3, new byte[0], message.getBytes());
        assertNotNull("MAC with empty key should not be null", mac);

        assertEquals("MAC length should be 32 bytes", 32, mac.length);
    }

    @Test
    public void testReinit() {
        String key = "TestKey123";
        String message = "Test message";
        HMAC hmac = new HMAC(HMAC.HMAC_SM3, key.getBytes());

        hmac.update(message.getBytes());
        byte[] mac1 = hmac.doFinal();
        assertNotNull("First MAC should not be null", mac1);

        hmac.reinit();
        hmac.update(message.getBytes());
        byte[] mac2 = hmac.doFinal();
        assertNotNull("Second MAC should not be null", mac2);

        assertArrayEquals("Reinitialized HMAC should produce same result", mac1, mac2);
    }

    @Test
    public void testMultiThread() throws InterruptedException {
        final String key = "TestKey123";
        final String message = "Test message";
        final byte[] expectedMac = HMAC.compute(HMAC.HMAC_SM3, key.getBytes(), message.getBytes());
        assertNotNull("Expected MAC should not be null", expectedMac);

        Thread[] threads = new Thread[2];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 10; j++) {
                    byte[] mac = HMAC.compute(HMAC.HMAC_SM3, key.getBytes(), message.getBytes());
                    assertNotNull("Thread MAC should not be null", mac);
                    assertArrayEquals("MAC should be consistent across threads", expectedMac, mac);
                }
            });
            threads[i].start();
        }

        for (Thread thread : threads) {
            thread.join();
        }
    }
}